#pragma once
#include "Cond.hpp"
#include "Mutex.hpp"
#include"Thread.hpp"
#include "Log.hpp"
#include <vector>
#include <string>
#include<queue>
// 这个文件的作用就是写一个线程池，这是头文件

//今天的任务就是把线程池完善一下，用单例模式进行书写

namespace ThreadPoolModule
{
    using namespace CondMoble;
    using namespace MutexMoble;
    using namespace LogModule;
    using namespace ThreadModle;
    static const int _grac  = 5;


    template<class T>
    class ThreadPool
    {
        void WakeupOne() //唤醒一个线程
        {
            _cond.Signal();
            LOG(LogLeval :: INFO) << "线程唤醒成功";
        }

        //唤醒所有的线程
        void WakeALLThread()
        {
            LockGuard s1(_mutex); //先上锁，防止多个线程同时进行唤醒处理
            if(_sleepnum)
                _cond.Brodcast();
            LOG(LogLeval :: INFO) << "唤醒了所有的线程";
        }

        void HardHeader()
        {
            char name[128];
            pthread_getname_np(pthread_self(),name,sizeof(name));
            while(1)
            {
                T t; //定义一个任务对象
                {
                    LockGuard s1(_mutex); //上锁处理    
                    //1.队列为空 ； 2.线程正在启动中
                    while(_task.empty() && _isrunning)
                    {
                        _sleepnum++;  //睡觉的线程+1
                        _cond.Wait(_mutex); //因为没任务，线程先睡一觉
                        _sleepnum--;  //醒来的线程-1
                    }
                    if(_task.empty() && !_isrunning)
                    {
                        LOG(LogLeval::INFO) << name << " 退出了, 线程池退出&&任务队列为空";
                        break;
                    }
                    t = _task.front();
                    _task.pop();
                }
                t();  //处理任务
            }
        }

        //使用懒汉方式进行单例模式的创建，此时因为只可以创建一个对象，所以要把构造函数私有化
        ThreadPool(int num = _grac)
        :_num(num),
        _isrunning(false)
        {
            for(int i = 0 ; i < _num  ; i++)
            {
                //往里面派发任务
                _thread.emplace_back(
                    [this]()
                {
                    HardHeader(); //回调函数，让它自己调
                });
            }
        }

        void Start()
        {
            if(_isrunning)
            return;  //判断此时的线程池是否是启动了，如果启动的话
            _isrunning = true;
            for(auto &it : _thread)
            {
                it.Start(); 
                LOG(LogLeval::INFO) << "start new thread success: " << it.Name();
            }
        }   //当然为了更加的安全，把start也要私有化

        ThreadPool(const ThreadPool<T> &) = delete;
        ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;

    public:
        static ThreadPool<T> *GetInstance()
        {
            if(inc == nullptr)
            {
                //上锁，因为是临界资源
                LockGuard s1(_mutex1);
                LOG(LogLeval :: DEBUG) << "获取单例";
                if(inc == nullptr)   //双重检测为了保证不会重复的利用锁，降低锁冲突的概率，提高性能
                {
                    LOG(LogLeval :: DEBUG) << "首次使用单例，创建...";
                    inc = new ThreadPool<T>();
                    inc->Start();
                }
            }
            return inc;
        }


        void Stop()
        {
            if(!_isrunning) return; //如果本来就是没在运行，不用重复关闭
            _isrunning = false;
            WakeALLThread(); //把所有的线程唤醒，完成它们的工作
        }
        bool Exqueue(const T& in)
        {
            //这个就相当于生产者，往里面放入数据
            if(_isrunning)
            {
                LockGuard s1(_mutex);
                _task.push(in);
                if(_task.size() == _sleepnum) //如果全睡着了的话那么就唤醒线程
                WakeupOne();
                return true;
            }
            return false;
        }

        void Join()
        {
            for(auto& thread : _thread)
            {
                thread.Join();
            }
        }
    private:
        std :: vector<Thread> _thread; //通过thread把Thread管理起来，里面存放的都是线程
        int _num; //线程池中的线程的个数
        std :: queue<T> _task; //存放任务的队列
        Cond _cond; //这个是为了让线程同步的
        Mutex _mutex; //这个是为了上锁，保护临界资源的


        bool _isrunning; //代表着此时的线程是否启动了
        int _sleepnum = 0; //代表着此时休眠的线程的总数

        //因为是单例模式，所以需要单例指针，并且锁也要为单例特别准备一个
        static ThreadPool<T> *inc;
        static Mutex _mutex1; //单例锁
    };

    //对于静态的成员变量，需要在类的外面进行定义，不然会出现链接错误
    template<class T>
    ThreadPool<T>* ThreadPool<T> :: inc = nullptr;
    template<class T>
    Mutex ThreadPool<T> :: _mutex1;
}